﻿using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using SHomeWorkshop.LunarPresentation.Expressions;

namespace SHomeWorkshop.LunarPresentation.Widegets
{
    public class ArrowPanel : Canvas
    {
        public ArrowPanel(Arrow parentArrowExpression)
        {
            this.parentArrowExpression = parentArrowExpression;
            this.Background = Brushes.Transparent;
            this.SizeChanged += new SizeChangedEventHandler(ArrowPanel_SizeChanged);
        }

        private Arrow parentArrowExpression;

        public Arrow ParentArrowExpression
        {
            get { return parentArrowExpression; }
            set { parentArrowExpression = value; }
        }

        private Path myPath;

        void ArrowPanel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateArrows();
        }

        private Brush foreground = Brushes.White;//默认白色。

        public Brush Foreground
        {
            get { return foreground; }
            set
            {
                foreground = value;
                UpdateArrows();
            }
        }

        public void UpdateArrows()
        {
            this.Children.Clear();
            if (parentArrowExpression == null) return;
            if (parentArrowExpression.ParentPanel == null) return;

            double halfOfWidth = this.ActualWidth / 2;
            double halfOfHeight = this.ActualHeight / 2;

            if (halfOfHeight <= 0 || halfOfWidth <= 0) return;

            double quarterOfWidth = halfOfWidth / 2;
            double quarterOfHeight = halfOfHeight / 2;

            myPath = new Path();
            myPath.MouseDown += new System.Windows.Input.MouseButtonEventHandler(myPath_MouseDown);
            myPath.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(myPath_MouseLeftButtonUp);
            myPath.Stroke = foreground;
            myPath.StrokeThickness = 2;
            myPath.StrokeLineJoin = PenLineJoin.Round;
            myPath.Fill = foreground;
            RenderOptions.SetClearTypeHint(myPath, ClearTypeHint.Enabled);

            StreamGeometry geometry = new StreamGeometry();
            //geometry.FillRule = FillRule.EvenOdd;

            // 判断对齐方向。
            ArrowDirection direction = ArrowDirection.Left;
            if (parentArrowExpression.ParentPanel.Direction == Orientation.Horizontal)
            {
                switch (parentArrowExpression.Arrows)
                {
                    case Arrow.ArrowLocation.Start:
                        {
                            direction = ArrowDirection.Up; break;
                        }
                    case Arrow.ArrowLocation.End:
                        {
                            direction = ArrowDirection.Down; break;
                        }
                    case Arrow.ArrowLocation.All:
                        {
                            direction = ArrowDirection.UpAndDown; break;
                        }
                    default:
                        {
                            direction = ArrowDirection.OnlyVLine; break;
                        }
                }
            }
            else
            {
                switch (parentArrowExpression.Arrows)
                {
                    case Arrow.ArrowLocation.Start:
                        {
                            direction = ArrowDirection.Left; break;
                        }
                    case Arrow.ArrowLocation.End:
                        {
                            direction = ArrowDirection.Right; break;
                        }
                    case Arrow.ArrowLocation.All:
                        {
                            direction = ArrowDirection.LeftAndRight; break;
                        }
                    default:
                        {
                            direction = ArrowDirection.OnlyHLine; break;
                        }
                }
            }

            DrawArrow(geometry, halfOfWidth, halfOfHeight, quarterOfWidth, quarterOfHeight, direction);

            geometry.Freeze();
            myPath.Data = geometry;

            this.Children.Add(myPath);
        }

        void myPath_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            this.OnMouseLeftButtonUp(e);
        }

        private void DrawArrow(StreamGeometry geometry, double halfOfWidth, double halfOfHeight,
            double quarterOfWidth, double quarterOfHeight, ArrowDirection direction)
        {
            using (StreamGeometryContext ctx = geometry.Open())
            {
                switch (direction)
                {
                    case ArrowDirection.Left:
                        {
                            Point apt = new Point(this.ActualWidth, halfOfHeight);
                            Point bpt = new Point(0, halfOfHeight);
                            Point cpt = new Point(halfOfHeight, quarterOfHeight);
                            Point dpt = new Point(halfOfHeight, halfOfHeight + quarterOfHeight);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(bpt);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.Right:
                        {
                            Point apt = new Point(0, halfOfHeight);
                            Point bpt = new Point(this.ActualWidth, halfOfHeight);
                            Point cpt = new Point(bpt.X - halfOfHeight, quarterOfHeight);
                            Point dpt = new Point(cpt.X, halfOfHeight + quarterOfHeight);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(bpt);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.LeftAndRight:
                        {
                            Point apt = new Point(halfOfHeight, halfOfHeight);
                            Point bpt = new Point(halfOfHeight, quarterOfHeight);
                            Point cpt = new Point(0, halfOfHeight);
                            Point dpt = new Point(halfOfHeight, halfOfHeight + quarterOfHeight);
                            Point ept = new Point(this.ActualWidth - halfOfHeight, halfOfHeight);
                            Point fpt = new Point(ept.X, ept.Y + quarterOfHeight);
                            Point gpt = new Point(this.ActualWidth, halfOfHeight);
                            Point hpt = new Point(ept.X, quarterOfHeight);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(apt);
                            pointList.Add(ept);
                            pointList.Add(fpt); pointList.Add(gpt); pointList.Add(hpt); pointList.Add(ept);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.OnlyHLine:
                        {
                            Point apt = new Point(this.ActualWidth, halfOfHeight);
                            Point bpt = new Point(0, halfOfHeight);
                            Point cpt = new Point(halfOfHeight, quarterOfHeight);
                            Point dpt = new Point(halfOfHeight, halfOfHeight + quarterOfHeight);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);
                            break;
                        }
                    case ArrowDirection.Up:
                        {
                            Point apt = new Point(halfOfWidth, this.ActualHeight);
                            Point bpt = new Point(halfOfWidth, 0);
                            Point cpt = new Point(quarterOfWidth, halfOfWidth);
                            Point dpt = new Point(halfOfWidth + quarterOfWidth, halfOfWidth);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(bpt);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.Down:
                        {
                            Point apt = new Point(halfOfWidth, 0);
                            Point bpt = new Point(halfOfWidth, this.ActualHeight);
                            Point cpt = new Point(quarterOfWidth, bpt.Y - halfOfWidth);
                            Point dpt = new Point(halfOfWidth + quarterOfWidth, cpt.Y);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(bpt);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.UpAndDown:
                        {
                            Point apt = new Point(halfOfWidth, halfOfWidth);
                            Point bpt = new Point(quarterOfWidth, halfOfWidth);
                            Point cpt = new Point(halfOfWidth, 0);
                            Point dpt = new Point(halfOfWidth + quarterOfWidth, halfOfWidth);
                            Point ept = new Point(halfOfWidth, this.ActualHeight - halfOfWidth);
                            Point fpt = new Point(ept.X + quarterOfWidth, ept.Y);
                            Point gpt = new Point(halfOfWidth, this.ActualHeight);
                            Point hpt = new Point(quarterOfWidth, ept.Y);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);

                            List<Point> pointList = new List<Point>();
                            pointList.Add(bpt); pointList.Add(cpt); pointList.Add(dpt); pointList.Add(apt);
                            pointList.Add(ept);
                            pointList.Add(fpt); pointList.Add(gpt); pointList.Add(hpt); pointList.Add(ept);

                            ctx.PolyLineTo(pointList, true, true);
                            break;
                        }
                    case ArrowDirection.OnlyVLine:
                        {
                            Point apt = new Point(halfOfWidth, this.ActualHeight);
                            Point bpt = new Point(halfOfWidth, 0);
                            Point cpt = new Point(quarterOfWidth, halfOfWidth);
                            Point dpt = new Point(halfOfWidth + quarterOfWidth, halfOfWidth);
                            ctx.BeginFigure(apt, true /* is filled */, true /* is closed */);
                            ctx.LineTo(bpt, true, true);
                            break;
                        }
                }
            }
        }

        public enum ArrowDirection { Up, Down, UpAndDown, Left, Right, LeftAndRight, OnlyHLine, OnlyVLine }

        void myPath_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            this.OnMouseDown(e);
        }
    }
}
